﻿/***************************************************************
 * 开发人员：网魂小兵@http://www.mozlite.com
 * 
 * 使用单键集合，主要用于多线程安全集合对象编程。
 * 
 * 最后修改：2011-10-25
 * ************************************************************/
namespace Mozlite.Collections
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// 双键锁对象集合。
    /// </summary>
    /// <typeparam name="TPrimaryKey">主键类型。</typeparam>
    /// <typeparam name="TSecondaryKey">次键类型。</typeparam>
    /// <typeparam name="TValue">锁定对象类型。</typeparam>
    public abstract class DualKeyLockbox<TPrimaryKey, TSecondaryKey, TValue> : SingleKeyLockbox<TPrimaryKey, TValue>
    {
        private readonly IDictionary<int, object> monitorLockbox;
        private readonly object monitorLocker = new object();
        private readonly IDictionary<TSecondaryKey, TPrimaryKey> secondaryLockbox;
        /// <summary>
        /// 初始化类<see cref="T:Mozlite.Collections.DualKeyLockbox{TPrimaryKey, TSecondaryKey, TValue}"/>。
        /// </summary>
        /// <param name="size">锁数量。</param>
        /// <param name="pkComparer">主键对比器。</param>
        /// <param name="skComparer">次键对比器。</param>
        public DualKeyLockbox(int size = 0, IEqualityComparer<TPrimaryKey> pkComparer = null, IEqualityComparer<TSecondaryKey> skComparer = null)
            : base(size, pkComparer)
        {
            this.secondaryLockbox = new Dictionary<TSecondaryKey, TPrimaryKey>(size, skComparer);
            this.monitorLockbox = new Dictionary<int, object>(size);
        }

        private TValue GetValue(TSecondaryKey skey)
        {
            TPrimaryKey primaryKeyFromValue;
            lock (base.SingleKeyLocker)
            {
                if (this.SecondaryLockbox.TryGetValue(skey, out primaryKeyFromValue))
                {
                    return base.PrimaryLockbox[primaryKeyFromValue];
                }
            }
            lock (this.GetSecondaryKeyLock(skey.GetHashCode()))
            {
                TValue value;
                lock (base.SingleKeyLocker)
                {
                    if (this.SecondaryLockbox.TryGetValue(skey, out primaryKeyFromValue))
                    {
                        return base.PrimaryLockbox[primaryKeyFromValue];
                    }
                }
                if (this.TryLoadBySecondaryKey(skey, out value))
                {
                    primaryKeyFromValue = this.GetPrimaryKeyFromValue(value);
                    this.SetValueForSecondary(primaryKeyFromValue, skey, value);
                }
                return value;
            }
        }
        /// <summary>
        /// 通过值添加到锁对象中。
        /// </summary>
        /// <param name="value">锁定对象实例。</param>
        public override void Add(TValue value)
        {
            this.SetValueForSecondary(this.GetPrimaryKeyFromValue(value), this.GetSecondaryKeyFromValue(value), value);
        }
        /// <summary>
        /// 清理所有锁列表。
        /// </summary>
        public override void Clear()
        {
            lock (base.SingleKeyLocker)
            {
                base.PrimaryLockbox.Clear();
                this.SecondaryLockbox.Clear();
            }
        }
        /// <summary>
        /// 判断是否包含次键。
        /// </summary>
        /// <param name="skey">次键实例对象。</param>
        /// <returns>返回判断结果。</returns>
        public bool ContainsSecondaryKey(TSecondaryKey skey)
        {
            lock (base.SingleKeyLocker)
            {
                return this.SecondaryLockbox.ContainsKey(skey);
            }
        }
        /// <summary>
        /// 通过值获取次键实例对象。
        /// </summary>
        /// <param name="value">锁值对象。</param>
        /// <returns>返回次键实例对象。</returns>
        protected abstract TSecondaryKey GetSecondaryKeyFromValue(TValue value);
        /// <summary>
        /// 通过哈希值获取锁定对象。
        /// </summary>
        /// <param name="hash">哈希值。</param>
        /// <returns>返回锁定对象。</returns>
        protected object GetSecondaryKeyLock(int hash)
        {
            return base.GetKeyLock(hash, this.monitorLocker, this.monitorLockbox);
        }
        /// <summary>
        /// 通过次键获取锁定对象。
        /// </summary>
        /// <param name="skey">次键。</param>
        /// <returns>返回锁定对象。</returns>
        public TValue GetValueBySecondary(TSecondaryKey skey)
        {
            TValue value = this.GetValue(skey);
            this.OnValueRetrieved(ref value);
            return value;
        }
        /// <summary>
        /// 通过主键移除锁定对象。
        /// </summary>
        /// <param name="pkey">主键。</param>
        /// <returns>返回移除结果。</returns>
        public override bool RemoveByPrimary(TPrimaryKey pkey)
        {
            lock (base.SingleKeyLocker)
            {
                TValue local;
                bool flag = base.PrimaryLockbox.TryGetValue(pkey, out local);
                if (flag)
                {
                    base.PrimaryLockbox.Remove(pkey);
                    this.SecondaryLockbox.Remove(this.GetSecondaryKeyFromValue(local));
                }
                return flag;
            }
        }
        /// <summary>
        /// 通过次键移除对象。
        /// </summary>
        /// <param name="skey">次键实例对象。</param>
        /// <returns>返回移除结果。</returns>
        public virtual bool RemoveBySecondary(TSecondaryKey skey)
        {
            lock (base.SingleKeyLocker)
            {
                TPrimaryKey local;
                bool flag = this.SecondaryLockbox.TryGetValue(skey, out local);
                if (flag)
                {
                    base.PrimaryLockbox.Remove(local);
                    this.SecondaryLockbox.Remove(skey);
                }
                return flag;
            }
        }
        /// <summary>
        /// 通过主键设置锁定对象。
        /// </summary>
        /// <param name="pkey">主键实例对象。</param>
        /// <param name="value">锁定对象实例。</param>
        protected override void SetValueForPrimary(TPrimaryKey pkey, TValue value)
        {
            this.SetValueForSecondary(pkey, this.GetSecondaryKeyFromValue(value), value);
        }
        /// <summary>
        /// 通过主次键设置锁定对象。
        /// </summary>
        /// <param name="pkey">主键实例对象。</param>
        /// <param name="skey">次键实例对象。</param>
        /// <param name="value">锁定对象实例。</param>
        protected virtual void SetValueForSecondary(TPrimaryKey pkey, TSecondaryKey skey, TValue value)
        {
            lock (base.SingleKeyLocker)
            {
                base.PrimaryLockbox[pkey] = value;
                this.SecondaryLockbox[skey] = pkey;
            }
        }
        /// <summary>
        /// 试图通过次键获取锁定实例对象。
        /// </summary>
        /// <param name="skey">次键实例对象。</param>
        /// <param name="value">返回锁定实例对象。</param>
        /// <returns>返回检索结果。</returns>
        protected abstract bool TryLoadBySecondaryKey(TSecondaryKey skey, out TValue value);
        /// <summary>
        /// 获取次键锁定字典列表。
        /// </summary>
        protected IDictionary<TSecondaryKey, TPrimaryKey> SecondaryLockbox
        {
            get
            {
                return this.secondaryLockbox;
            }
        }
    }
}
